package com.auth.manage.domain.service;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.auth.manage.constant.AuthPortConstant;
import com.auth.manage.domain.entity.AuthCode;
import com.auth.manage.domain.entity.AuthPort;
import com.auth.manage.infr.exception.errorcode.AuthorizationErrorCode;
import com.auth.manage.infr.exception.errorcode.AuthorizationPortErrorCode;
import com.auth.manage.infr.exception.AuthorizationPortException;
import com.auth.manage.infr.mapper.AuthorizationPortMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@RequiredArgsConstructor
@Service
public class AuthorizationPortService extends ServiceImpl<AuthorizationPortMapper, AuthPort> {
    private AuthorizationService authorizationService;

    public List<AuthPort> getPortsByCdk(String cdk) {
        return  lambdaQuery()
                .eq(AuthPort::getAuthCodeCdk, cdk)
                .list();
    }

    // 根据优先级逻辑删除
    public boolean deleteBeyondPortLimitPorts(AuthCode authCodeId,Integer num ){
        return lambdaUpdate()
                .set(AuthPort::getAuthCodeId, null)
                .eq(AuthPort::getAuthCodeId, authCodeId)
                .orderBy(true, false, AuthPort::getPriority)
                .last("LIMIT " + num)
                .update();
    }


    public void addPort(Integer portNum, Long priority, String cdk, Long userId) {
        // todo 一条sql：要求端口表和授权码表是同一个服务的表
        // todo 非同一个服务下，可以有延时任务，补偿对齐数据
        // 添加端口要求必须指定该端口所属的授权码，否则无意义
        // 1.判断当前授权码是否到达了端口上限
        // 1.1 查询授权码的端口上限
        // todo 是否改为查询授权码状态为正常的端口数
        AuthCode authCode = authorizationService.getAuthCodeByCdk(cdk);
        Long limit = authCode.getBindPortLimit();
        // 1.2 查询授权码已绑定的端口数：端口表中查询
        Long num = getBindPortNumByAuthCodeCdk(cdk);
        // 1.3 要求授权码的端口上限大于该授权码已绑定的端口数
        // limit 变小了，num变大了
        Assert.isTrue(limit > num, () ->new AuthorizationPortException(AuthorizationErrorCode.ON_PORT_LIMIT));
        // 1.4 查询授权码对应的主键id
        Long authCodeId = authCode.getId();
        // 2.封装AuthPort对象
        AuthPort authPort = new AuthPort();
        authPort.saveAuthPort(userId, authCodeId, cdk, portNum, priority);
        // 3.保存数据库
        Assert.isTrue(save(authPort), () -> new AuthorizationPortException(AuthorizationPortErrorCode.OPERATION_FAIL));
    }

    // 根据授权码的主键id，批量删除指定的端口
    public boolean deleteByAuthCodeId(Long authCodeId, List<Integer> portIds) {
        Long userId = (Long) StpUtil.getLoginId();
        return lambdaUpdate()
                .in(AuthPort::getId, portIds)
                .eq(AuthPort::getUserId, userId)
                .eq(AuthPort::getAuthCodeId, authCodeId)
                .remove();
    }

    // 根据授权码cdk，批量删除指定的端口
    public boolean deleteByAuthCode(String cdk, List<Integer> portIds) {
        // todo 粒度小且伴随多次数据库操作就加事务
        Long userId = (Long) StpUtil.getLoginId();

        return lambdaUpdate()
                .in(AuthPort::getId, portIds)
                .eq(AuthPort::getUserId, userId)
                .eq(AuthPort::getAuthCodeCdk, cdk)
                .remove();
    }

    // 修改端口号
    public void updatePort(Long userId, Integer originalPortId, Integer port) {
        // 1.根据授权码字符串查询得到授权码对象，获取授权码主键id
        boolean update = lambdaUpdate()
                .eq(AuthPort::getUserId, userId)
                .eq(AuthPort::getId, originalPortId)
                .set(AuthPort::getPortNum, port)
                .update();
        Assert.isTrue(update, () -> new AuthorizationPortException(AuthorizationPortErrorCode.OPERATION_FAIL));
    }

    // 查询授权码已绑定的端口数
    public Long getBindedPortNumByAuthCodeId(Long authCodeId) {
        return lambdaQuery()
                .eq(AuthPort::getAuthCodeId, authCodeId)
                .eq(AuthPort::getStatus, AuthPortConstant.VALID)
                .count();


    }
    public Long getBindPortNumByAuthCodeCdk(String cdk) {
        return lambdaQuery()
                .eq(AuthPort::getAuthCodeCdk, cdk)
                .count();
    }

    public boolean unBindIpAndPortsByAuthCodeIds(List<Long> authCodeIds) {
        // 将IP和端口字段置空
        return lambdaUpdate()
                .set(AuthPort::getIp, null)
                .set(AuthPort::getPortNum, null)
                .in(AuthPort::getAuthCodeId, authCodeIds)
                .update();
    }


}
